<template>
  <article class="page page-jssfzip-webserver">
    <h4 class="text-center">通过WEB服务访问ZIP压缩包</h4>
    <form class="form">
      <div class="form-group">
        <fieldset>
          <legend>参数设置</legend>
          <div class="row-flex" align="center">
            <div class="row-flex-prefix">入口文件</div>
            <div class="row-flex-main"><input class="form-control" type="text" v-model="zipPath" placeholder="请选择zip/zip_encrypted文件" readonly /></div>
            <div class="row-flex-suffix">
              <x-button :loading="loadingCount > 0" @click="setZipPath">选择</x-button>
            </div>
          </div>
          <div class="row-flex" align="center">
            <div class="row-flex-prefix">分包文件</div>
            <div class="row-flex-main">
              <textarea class="form-control" v-model="zipChildPathStr" rows="3" readonly placeholder="请选择zip/zip_encrypted文件"></textarea>
            </div>
            <div class="row-flex-suffix">
              <x-button :loading="loadingCount > 0" @click="setZipChildPath">选择</x-button>
            </div>
          </div>
          <div class="row-flex" align="center">
            <div class="row-flex-prefix">分包前缀</div>
            <div class="row-flex-main"><input class="form-control" type="text" v-model="zipChildPathPrefix" /></div>
          </div>
          <div class="row-flex" align="center">
            <div class="row-flex-prefix">加密方式</div>
            <div class="row-flex-main text-left">
              <div class="form-check form-check-inline" v-for="(v, i) in encryptTypeList" :key="i">
                <input class="form-check-input" type="radio" :value="v.value" v-model="encryptType" :disabled="loadingCount > 0">
                <label class="form-check-label">{{ v.label }}</label>
              </div>
            </div>
            <div class="row-flex-suffix" style="width: 8em;"></div>
          </div>
          <div class="row-flex" align="center">
            <div class="row-flex-prefix">服务端口</div>
            <div class="row-flex-main"><input class="form-control" type="text" v-model="port" :disabled="loadingCount > 0" /></div>
          </div>
          <div class="row-flex">
            <div class="row-flex-prefix"></div>
            <div class="row-flex-main">
              <x-button v-if="loadingCount > 0" type="primary" block @click="serverStop">关闭服务</x-button>
              <x-button v-else type="primary" block @click="serverStart">启动服务</x-button>
            </div>
          </div>
        </fieldset>
      </div>
      <div class="form-group form-group-log">
        <fieldset>
          <legend>打印日志</legend>
          <div class="fieldset-content">
            <textarea class="form-control" ref="textarea" v-model.trim="result" rows="10" readonly></textarea>
          </div>
        </fieldset>
      </div>
    </form>
  </article>
</template>

<script>
const fs = global.require('fs')
const path = global.require('path')
const {remote} = global.require('electron')
const {dialog} = remote
const selfWindow= remote.getCurrentWindow()
let JSZip = global.JSZip || null
let express = null
let httpServer = null
let zipDataDict = {}

export default {
  name: 'jssfzip-webserver',
  data () {
    return {
      port: 5858,
      encryptType: '1',
      encryptTypeList: [
        {value: '1', label: '无'},
        {value: '2', label: '京师书法课件', password: 'l6fwZHdJbU2Y4jxPDwjoI6P9vltHhc8bvDlExm4vRRg='},
        {value: '3', label: '京师(快)', password: 0xFB},
        {value: '4', label: '京师(慢)', password: 0xFB}
      ], // 可用的加密模式列表
      zipPath: '',
      zipChildPathStr: '',
      zipChildPathPrefix: '',
      zipChildPathArr: [],
      result: '',
      loadingCount: 0
    }
  },
  computed: {
    encryptTypeDict () {
      let _dict = {}
      this.encryptTypeList.forEach(v => {
        _dict[v.value] = v.password || ''
      })
      return _dict
    },
    zipPathDict () {
      let _dict = {main: this.zipPath}
      this.zipChildPathArr.forEach(v => {
        let _fileName = path.basename(v)
        let _arr = _fileName.split('.')
        let _key = v
        if (_arr.length > 1) {
          _arr = _arr.slice(0, _arr.length - 1)
          _key = _arr.join('.')
        }
        _dict[_key] = v
      })
      return _dict
    }
  },
  watch: {
    result () {
      this.$refs.textarea && (this.$refs.textarea.scrollTop = 10000)
    }
  },
  mounted () {
    express = this.$sharedObject('express')
  },
  beforeDestroy () {
    this.serverStop()
  },
  methods: {
    // 入口ZIP文件选择
    setZipPath () {
      let filePaths = dialog.showOpenDialogSync(selfWindow, {
        title: '选择文件',
        buttonLabel:'确定',
        defaultPath: this.$sharedObject('desktopPath'),
        properties: ['openFile']
      })
      if (filePaths instanceof Array && filePaths.length > 0) {
        this.zipPath = filePaths[0]
      }
    },
    // 分包ZIP文件选择
    setZipChildPath () {
      let filePaths = dialog.showOpenDialogSync(selfWindow, {
        title: '选择文件',
        buttonLabel:'确定',
        defaultPath: this.$sharedObject('desktopPath'),
        properties: ['openFile', 'multiSelections']
      })
      if (filePaths instanceof Array && filePaths.length > 0) {
        this.zipChildPathArr = [...filePaths]
        this.zipChildPathStr = this.zipChildPathArr.join('\n')
      }
    },
    // 资源解密
    decrypt (data) {
      let _data = data
      let password = this.encryptTypeDict[this.encryptType]
      if (this.encryptType === '2') {
        password = Buffer.from(password, 'base64')
        _data = this.$sharedObject('jstoolsNode').encrypt(password, _data)
      } else if (this.encryptType === '3') {
        let encrypted = Buffer.from(_data)
        let length = 10
        for (let i = 0; i < length; i++) {
          encrypted[i] = encrypted[i] ^ password
        }
        _data = encrypted
      } else if (this.encryptType === '4') {
        let encrypted = Buffer.from(_data)
        let length = encrypted.length
        for (let i = 0; i < length; i++) {
          encrypted[i] = encrypted[i] ^ password
        }
        _data = encrypted
      }
      return _data
    },
    // 读取资源+解密=>jszip对象
    loadZipItem (zipName) {
      this.result += `读取文件:${zipName}` + '\n'
      let zipPath = this.zipPathDict[zipName]
      let zipData = fs.readFileSync(zipPath)
      zipData = this.decrypt(zipData)
      this.result += '解析文件...' + '\n'
      return JSZip.loadAsync(zipData).then((zip) => {
        zipDataDict[zipName] = zip
        return zip
      }).catch((err) => {
        console.error('解析 zip/zip_encrypted 文件时出错:', err);
        this.result += '解析文件时出错' + '\n'
      })
    },
    // 根据path获取jszip数据对象
    async getZipData (_path) {
      let _arr = _path.split('/')
      if (_arr.length === 1 || !this.zipPathDict[_arr[0]]) { return zipDataDict['main'] }
      if (!zipDataDict[_arr[0]]) {
        await this.loadZipItem(_arr[0])
      }
      return zipDataDict[_arr[0]]
    },
    // 启动服务
    async serverStart () {
      this.result = ''
      this.port = parseInt(this.port) || 6666
      if (!this.zipPath || !fs.existsSync(this.zipPath)) {
        this.result += '请选择入口文件' + '\n'
        return
      }
      this.result = '启动服务...' + '\n'
      let app = express()
      await this.loadZipItem('main')
      this.loadingCount++
      app.get('/:path(*)', async (req, res) => {
        let _path = req.params.path
        if (this.zipChildPathPrefix && _path.indexOf(this.zipChildPathPrefix) === 0) {
          _path = _path.substring(this.zipChildPathPrefix.length)
        }
        let _zip = await this.getZipData(_path)
        let _file = _zip.file(_path)
        let _fileName = path.basename(_path)
        this.result += `收到请求:${_path}` + '\n'
        if (_file) {
          _file.async('nodebuffer').then((content) => {
            res.type(_fileName)
            res.send(content)
          }).catch((err) => {
            this.result += `读取文件内容时出错:${_path}` + '\n'
            console.error('读取文件内容时出错:', err)
            res.status(500).send('读取文件内容时出错')
          })
        } else {
          this.result += `文件未找到:${_path}` + '\n'
          res.status(404).send('文件未找到')
        }
      })
      app.listen(this.port, () => {
        this.result += `服务器运行在 http://localhost:${this.port}` + '\n'
      })
    },
    serverStop () {
      this.result += '关闭服务...' + '\n'
      httpServer && httpServer.close()
      this.loadingCount = 0
    }
  }
}
</script>

<style scoped lang="less">
@import "~@/assets/css/_mixins.less";
@import "~@/assets/css/_color.less";
.row-flex {
  & + .row-flex {
    margin-top: .5em;
  }
  .row-flex-prefix {
    width: 6em;
    text-align: right;
  }
}
.form {
  height: calc(100vh - 137px);
  display: flex;
  flex-direction: column;
  .form-group-log {
    margin-top: .5em;
    height: 0;
    flex-grow: 1;
    fieldset {
      height: 100%;
      .fieldset-content {
        height: calc(100% - 2em);
        textarea {
          height: 100%;
        }
      }
    }
  }
  .x-button {
    min-width: 8em;
  }
}
</style>
